package com.keyou.reggie.controller;

import com.keyou.reggie.common.ResultInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.UUID;

/**
 * @Author: klq
 * @Date: 2023/1/9 16:25
 * @Description:
 */

/**
 * 先我们要认识一下Java中的流：
 * 1、“流”是一个抽象的概念，它是对输入输出设备的一种抽象理解，在java中，对数据的输入输出操作都是以“流”的方式进行的。
 * 2、“流”具有方向性，输入流、输出流是相对的。当程序需要从数据源中读入数据的时候就会开启一个输入流，相反，写出数据到某个数据源目的地的时候也会开启一个输出流。
 * 3、数据源可以是文件、内存或者网络等。
 */
@Slf4j
@RestController
@RequestMapping(value = "/common")
public class CommonController {

    @Value("${reggie.upload-path}")
    private String uploadPath;

    @GetMapping("/hello")
    public ResultInfo<Object> hello() {
        return ResultInfo.success("hello Spring Boot");
    }

    /**
     * 文件上传时，对页面的form表单有如下要求：
     * 1、method="post"，采用post方式提交数据
     * 2、enctype="multipart/form-data"，采用multipart格式上传文件
     * 3、type="file"，使用input的file控件上传
     * <p>
     * MultipartFile工具类，SpringMVC提供简化上传操作的工具类。
     * multipartFile是个临时文件，我们在断点调试的时候可以看到，执行完整个方法之后就消失了
     *
     * @return
     */
    @PostMapping("/upload")
    public ResultInfo<String> uploadFiles(@RequestParam(value = "file") MultipartFile multipartFile) {
        log.info("上传文件，参数的名称：{}、内容的类型：{}、内容的大小：{}", multipartFile.getName(), multipartFile.getContentType(), multipartFile.getSize());
        //创建文件对象
        File dir = new File(uploadPath);
        //判断当前目录是否存在，不存在则新建
        if (!dir.exists()) {
            dir.mkdirs();
        }
        //获取传入文件的昵称
        String originalFilename = multipartFile.getOriginalFilename();
        //我们只需要获取一下格式后缀，取子串，起始点为最后一个.
        assert originalFilename != null;
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        //为了防止出现重复的文件名，我们需要使用UUID
        String fileName = UUID.randomUUID() + suffix;
        try {
            //使用transferTo(dest)，将上传文件写到服务器上指定的目录;
            //使用此方法保存必须要绝对路径且文件夹必须已存在,否则报错
            multipartFile.transferTo(new File(uploadPath + fileName));
        } catch (IOException e) {
            e.printStackTrace();
            //这里最好将错误结果抛给前端
            throw new RuntimeException(e);
        }
        //将文件名返回给前端，便于后期的开发
        return ResultInfo.success(fileName);
    }

    /**
     * 文件下载：
     * 1、通过输入流读取文件内容
     * 2、通过输出流将文件写回浏览器，在浏览器展示图片
     * 3、关闭输入输出流，释放资源
     *
     * @param response
     * @param fileName
     */
    @GetMapping("/download")
    public void downloadFile(HttpServletResponse response, @RequestParam(value = "name") String fileName) {
        FileInputStream fileInputStream = null;
        ServletOutputStream outputStream = null;
        try {
            //建立连接
            fileInputStream = new FileInputStream(uploadPath + fileName);
            outputStream = response.getOutputStream();

            response.setContentType("image/jpeg");

            int length;
            byte[] buffer = new byte[1024];
            //1M=1024k，1K=1024字节，一个字节通常8位长
            //read(byte b[]) 从输入流中读取b.length个字节（二进制）到字节数组中，返回读入缓冲区的总字节数（二进制转成十进制的整数返回），若到达文件末尾，则返回-1
            //使用read(byte b[])方法时，数组b的长度至关重要，若长度小于流的字节长度，那么最后得出的内容会出现丢失。若大于流的字节长度，那么最后数组的内存就浪费了，那么就需要根据文件的字节长度来设置数组的长度
            while ((length = fileInputStream.read(buffer)) != -1) {
                //输出字节流
                outputStream.write(buffer, 0, length);
            }
        } catch (IOException e) {
            throw new RuntimeException();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException();
                }
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException();
                }
            }
        }
    }


}